package com.biz.primus.ms.promotion.dao.po;

import com.biz.primus.base.enums.PromotionTypeEnum;
import com.biz.primus.base.exception.utils.AssertUtils;
import com.biz.primus.common.utils.JsonUtils;
import com.biz.primus.model.promotion.enums.PromotionPaymentChannelEnum;
import com.biz.primus.model.promotion.enums.PromotionTargetLimitTypeEnum;
import com.biz.primus.model.promotion.exception.PromotionExceptionType;
import com.biz.primus.model.promotion.vo.DiscountProductVo;
import com.biz.primus.model.promotion.vo.DiscountPromotionRuleVo;
import com.biz.primus.model.promotion.vo.OrderPromotionDetailVo;
import com.biz.primus.model.promotion.vo.req.OrderPromotionReqDetailVo;
import com.google.common.collect.Lists;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.persistence.*;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

/**
 * 折扣促销
 *
 * @author david-liu
 */
@Entity
@Getter
@Setter
public class DiscountPromotion extends BasePromotion {
    private static final long serialVersionUID = 126011329708554976L;

    // 是否阶梯折扣
    @Column
    private Boolean isStairCut = false;

    /**
     * 存放折扣促销规则Vo JSON信息
     *
     * @see DiscountPromotionRuleVo
     */
    @Column(columnDefinition = "TEXT")
    private String discountPromotionRule;

    // 支付渠道限制
    @Enumerated(value = EnumType.STRING)
    private PromotionPaymentChannelEnum paymentChannel;

    @Transient
    private DiscountPromotionRuleVo discountPromotionRuleVo;

    public DiscountPromotionRuleVo getDiscountPromotionRule() {
        AssertUtils.isTrue(StringUtils.isNotBlank(this.discountPromotionRule), PromotionExceptionType.ILLEGAL_STATE);
        if (Objects.isNull(this.discountPromotionRuleVo)) {
            this.discountPromotionRuleVo = JsonUtils.json2Obj(this.discountPromotionRule, DiscountPromotionRuleVo.class);
        }
        DiscountPromotionRuleVo discountPromotionRuleVo = this.discountPromotionRuleVo;
        AssertUtils.isTrue(Objects.nonNull(discountPromotionRuleVo.getIsStairCut()) && Objects.nonNull(discountPromotionRuleVo.getLimitType()) && MapUtils.isNotEmpty(discountPromotionRuleVo.getDiscountRates()), PromotionExceptionType.ILLEGAL_STATE);
        return discountPromotionRuleVo;
    }

    @Override
    public boolean isMatchPromotionRule(OrderPromotionReqDetailVo detailVo) {
        AssertUtils.isTrue(Objects.nonNull(detailVo), PromotionExceptionType.ILLEGAL_PARAMETER);
        detailVo.postProcess();
        DiscountPromotionRuleVo discountPromotionRule = this.getDiscountPromotionRule();
        Map.Entry<Integer, BigDecimal> entry = discountPromotionRule.getDiscountRates().entrySet().iterator().next();
        AssertUtils.isTrue(Objects.nonNull(entry) && Objects.nonNull(entry.getKey()), PromotionExceptionType.ILLEGAL_STATE);
        List<Long> effectProductIds = this.getPromotionEffectProductIds(detailVo);
        if (CollectionUtils.isEmpty(effectProductIds)) {
            return false;
        }
        if (discountPromotionRule.getLimitType() == PromotionTargetLimitTypeEnum.DISCOUNT_BY_PRODUCT_AMOUNT) {
            return entry.getKey() <= effectProductIds.stream().map(detailVo::getProductAmount).reduce(0, (acc, item) -> acc = acc + item);
        } else {
            return entry.getKey() <= effectProductIds.stream().map(detailVo::getRequestProductQuantity).reduce(0, (acc, item) -> acc = acc + item);
        }
    }

    @Override
    public OrderPromotionDetailVo getOrderPromotionDetail(OrderPromotionReqDetailVo reqDetailVo) {
        AssertUtils.isTrue(Objects.nonNull(reqDetailVo), PromotionExceptionType.ILLEGAL_PARAMETER);
        reqDetailVo.postProcess();
        if (!isMatchPromotionRule(reqDetailVo)) {
            return null;
        }
        List<Long> promotionEffectProductIds = this.getPromotionEffectProductIds(reqDetailVo);
        OrderPromotionDetailVo detailVo = new OrderPromotionDetailVo();
        detailVo.setId(this.getId());
        detailVo.setName(this.getName());
        detailVo.setDescription(this.getDescription());
        detailVo.setAllowCoupon(this.getAllowCoupon());
        detailVo.setPromotionType(PromotionTypeEnum.DISCOUNT);
        detailVo.setPaymentChannelLimit(this.getPaymentChannel());
        detailVo.setEffectProductIds(promotionEffectProductIds);
        DiscountPromotionRuleVo discountPromotionRule = this.getDiscountPromotionRule();
        Integer discountCalculateCondition;
        if (discountPromotionRule.getLimitType() == PromotionTargetLimitTypeEnum.DISCOUNT_BY_PRODUCT_AMOUNT) {
            discountCalculateCondition = promotionEffectProductIds.stream().map(reqDetailVo::getProductAmount).reduce(0, (acc, item) -> acc = acc + item);
        } else {
            discountCalculateCondition = promotionEffectProductIds.stream().map(reqDetailVo::getRequestProductQuantity).reduce(0, (acc, item) -> acc = acc + item);
        }
        Map.Entry<Integer, BigDecimal> matchingDiscountRate = null;
        if (discountPromotionRule.getIsStairCut()) {
            for (Map.Entry<Integer, BigDecimal> promotionDiscountRateEntry : this.getDiscountPromotionRule().getDiscountRates().entrySet()) {
                if (Objects.isNull(matchingDiscountRate) || matchingDiscountRate.getKey() <= discountCalculateCondition) {
                    matchingDiscountRate = promotionDiscountRateEntry;
                } else {
                    break;
                }
            }
        } else {
            matchingDiscountRate = this.getDiscountPromotionRule().getDiscountRates().entrySet().iterator().next();
        }
        AssertUtils.isTrue(Objects.nonNull(matchingDiscountRate), PromotionExceptionType.ILLEGAL_STATE);
        List<DiscountProductVo> discountProductVos = Lists.newArrayList();
        for (Long promotionEffectProductId : promotionEffectProductIds) {
            DiscountProductVo discountProductVo = new DiscountProductVo();
            discountProductVo.setProductId(promotionEffectProductId);
            discountProductVo.setOriginalProductAmount(reqDetailVo.getProductAmount(promotionEffectProductId));
            discountProductVo.setDiscountProductAmount(new BigDecimal(reqDetailVo.getProductAmount(promotionEffectProductId)).multiply(matchingDiscountRate.getValue()).intValue());
            discountProductVos.add(discountProductVo);
        }
        detailVo.setDiscountProducts(discountProductVos);
        return detailVo;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
    }
}
